#include "jpg.h"
#include "bmp.h"
#include "lcd.h"

void init_lcd(struct lcd_info *lcdinfo)
{
    lcdinfo->fd = open("/dev/fb0", O_RDWR);
    if(lcdinfo->fd == -1)
    {
        perror("打开LCD设备失败");
        exit(0);
    }

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    // 获取LCD硬件参数
    ioctl(lcdinfo->fd, FBIOGET_VSCREENINFO, &vinfo);
    ioctl(lcdinfo->fd, FBIOGET_FSCREENINFO, &finfo);

    lcdinfo->width = vinfo.xres;    
    lcdinfo->height= vinfo.yres;    
    lcdinfo->bpp   = vinfo.bits_per_pixel;    
    int screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel/8;

    // 将映射内存设置为 800 * 480
    lcdinfo->fb_mem = mmap(NULL, screensize, PROT_READ|PROT_WRITE,
                           MAP_SHARED, lcdinfo->fd, 0);
    if(lcdinfo->fb_mem == MAP_FAILED)
    {
        perror("内存映射失败");
        exit(0);
    }
}

// 约定俗成的做法：内部（本模块）使用的函数，用双下划线开头
void __clean_screen(struct lcd_info *lcdinfo)
{
    // 清屏，白屏
    int screensize = lcdinfo->width * lcdinfo->height * lcdinfo->bpp/8;
    memset(lcdinfo->fb_mem, 0xFFFFFFFF, screensize);
}

void show_jpg(struct lcd_info *lcdinfo, struct jpg_info *jpginfo)
{
    __clean_screen(lcdinfo);

    // 居中指针
    int x, y;
    x = (lcdinfo->width  - jpginfo->width) /2;
    y = (lcdinfo->height - jpginfo->height)/2;
    char *tmp = lcdinfo->fb_mem + (800*(y-1) + x)*4;

    int wlcd = lcdinfo->width  - x;
    int hlcd = lcdinfo->height - y;

    for(int h=0; h<jpginfo->height && h<hlcd; h++)
    {
        int lcd_offset = lcdinfo->width * 4 * h;
        int rgb_offset = jpginfo->width * 3 * h;

        // 显示一行
        for(int w=0; w<jpginfo->width && w<wlcd; w++)
        {
            memcpy(tmp + 4*w + lcd_offset+2, jpginfo->rgb + 3*w + rgb_offset+0, 1);
            memcpy(tmp + 4*w + lcd_offset+1, jpginfo->rgb + 3*w + rgb_offset+1, 1);
            memcpy(tmp + 4*w + lcd_offset+0, jpginfo->rgb + 3*w + rgb_offset+2, 1);
        }
    }
}

void show_bmp(struct lcd_info *lcdinfo, struct bmp_info *bmpinfo)
{
    __clean_screen(lcdinfo);

    // 居中指针
    int x, y;
    x = (lcdinfo->width  - bmpinfo->width) /2;
    y = (lcdinfo->height - bmpinfo->height)/2;
    char *tmp = lcdinfo->fb_mem + (800*(y-1) + x)*4;

    int wlcd = lcdinfo->width  - x;
    int hlcd = lcdinfo->height - y;

    // 计算无效字节
    int linesize = bmpinfo->width*bmpinfo->bpp/8;
    int pad = (4-linesize%4) % 4; // 0 ~ 3

    // 取最底一行的指针
    char *rgb = bmpinfo->rgb + (linesize+pad)*(bmpinfo->height-1);

    // 4-> 按照BMP的特性将RGB显示出来
    int w, h;
    for(w=0, h=0; h<bmpinfo->height && h<wlcd; h++)
    {
        unsigned lcd_offset = h * lcdinfo->width*4;
        
        // 显示一行
        for(w=0; w<bmpinfo->width && w<wlcd; w++)
            memcpy(tmp + w*4 + lcd_offset, rgb + w*3 , 3);

        rgb -= (linesize + pad);
    }
}

void show_png()
{
    //...
}

void release_lcd(struct lcd_info *lcdinfo)
{
    // ..
}